﻿using DefaultDspPlugins.UI.Common.Modulizer.Engine.GUIv2.Controls;
using SharpDepend.Datatypes;
using System;
using System.Collections.Generic;
using ZquenceStudio3Host.DspPlugins.Synthesizers.Nodesizer;
using ZquenceStudio3Host.DspPlugins.Synthesizers.Nodesizer.Engine;
using ZquenceStudio3Host.DspPlugins.Synthesizers.Nodesizer.Engine.Modules.Container;
using ZquenceStudio3Host.Graphics.Components;
using ZquenceStudio3Host.Graphics.GUI;

namespace DefaultDspPlugins.UI.Common.Modulizer.Engine.GUIv2.Editors
{
    class ParameterEditItem : ListboxItem
    {
        private ModulizerUIv2 mModulizerUI;
        private ModuleParameter mParameter;
        private BaseModule mModule;
        private ModuleIO mIO;
        private Button mInternally;
        private Panel panel;
        private Dial mDial;

        internal static bool ToggleNextColor;

        public ParameterEditItem(BasicGuiManager engineCore, ModulizerUIv2 modulizerUI, ModuleIO io)
            : base(engineCore)
        {
            mIO = io;
            mModule = io.Module;

            FloatingPanel dial = new FloatingPanel(engineCore);
            dial.DimensionInProcent = true;
            dial.Y = 30;
            dial.Height = 40;
            dial.Width = 25;
            dial.FloaterTexture = ZquenceStudio3Host.Graphics.Factory.TextureCircle;
            dial.setFloaterColor(ZquenceStudio3Host.DspPlugins.Synthesizers.Nodesizer.Engine.GUI.BaseModulizerUI.GetColor(io));

            ItemObject = io;
            Init(engineCore, modulizerUI, dial);
        }

        public ParameterEditItem(BasicGuiManager engineCore, ModulizerUIv2 modulizerUI, ModuleParameter parameter)
            : base(engineCore)
        {
            mParameter = parameter;
            mModule = parameter.Module;

            mDial = new Dial(engineCore);
            mDial.BackgroundColor = Color.Transparent;
            mDial.SetParameter(parameter, modulizerUI, null);
            mDial.DimensionInProcent = true;
            mDial.Height = 100;
            mDial.Width = 25;

            ItemObject = parameter;
            Init(engineCore, modulizerUI, mDial);

            // Edit min.
            Button editMinButton = FrontEndPanel.GetBaseButton(engineCore);
            editMinButton.DimensionInProcent = true;
            editMinButton.Width = 20;
            editMinButton.Height = 47;
            editMinButton.X = 60;
            editMinButton.Y = 2;
            editMinButton.Label.Text = "Min";
            editMinButton.OnClicked += EditMinButton_OnClicked;
            panel.AddComponent(editMinButton);

            // Edit max.
            Button editMaxButton = FrontEndPanel.GetBaseButton(engineCore);
            editMaxButton.DimensionInProcent = true;
            editMaxButton.Width = 20;
            editMaxButton.Height = 47;
            editMaxButton.X = 80;
            editMaxButton.Y = 2;
            editMaxButton.Label.Text = "Max";
            editMaxButton.OnClicked += EditMaxButton_OnClicked;
            panel.AddComponent(editMaxButton);
        }

        private void EditMinButton_OnClicked(object sender, double x, double y, int id)
        {
            SharpDepend.Manager.DialogManager.Input("Change min parameter value", "Change the minimum value for parameter '" + mParameter.Label + "'", mParameter.MinValue.ToString(), ChangeMinResult, mParameter);
        }

        private void EditMaxButton_OnClicked(object sender, double x, double y, int id)
        {
            SharpDepend.Manager.DialogManager.Input("Change max parameter value", "Change the maximum value for parameter '" + mParameter.Label + "'", mParameter.MaxValue.ToString(), ChangeMaxResult, mParameter);
        }

        void ChangeMinResult(bool success, string input, object parameter)
        {
            if (success)
            {
                ChangeValue(input, delegate (double value)
                {
                    mParameter.MinValue = value;
                    mDial.UpdateParameter();

                    // Update module UI.
                    mModulizerUI.UpdateModuleUIUp(mModule);
                });
            }
        }

        void ChangeMaxResult(bool success, string input, object parameter)
        {
            if (success)
            {
                ChangeValue(input, delegate (double value)
                {
                    mParameter.MaxValue = value;
                    mDial.UpdateParameter();

                    // Update module UI.
                    mModulizerUI.UpdateModuleUIUp(mModule);
                });
            }
        }

        private void ChangeValue(string input, Action<double> action)
        {
            double value;
            if (double.TryParse(input, out value))
            {
                action(value);
            }
            else
            {
                SharpDepend.Manager.DialogManager.Show("Coul'd not use this value '" + input + "'. It has to be a number.", "Parse error", SharpDepend.DialogIcon.Error);
            }
        }

        private void Init(BasicGuiManager engineCore, ModulizerUIv2 modulizerUI, Panel itemPanel)
        {
            mModulizerUI = modulizerUI;

            panel = new Panel(engineCore);

            if (ToggleNextColor = !ToggleNextColor)
            {
                panel.BackgroundColor = Color.Transparent;
            }
            else
            {
                panel.BackgroundColor = new Color(80, 34, 34, 34);
            }

            // Label.
            Label label = new Label(engineCore);
            label.DimensionInProcent = true;
            label.Width = 75;
            label.Height = 30;
            label.X = 25;
            label.Y = 15;
            label.AutoSize = true;
            label.TextColor = Color.Black;
            label.Text = mParameter != null ? mParameter.Label : mIO.ShortName;

            // Internally button.
            mInternally = FrontEndPanel.GetBaseButton(engineCore);
            mInternally.DimensionInProcent = true;
            mInternally.Width = 45;
            mInternally.Height = 48;
            mInternally.X = 25;
            mInternally.Y = 50;
            mInternally.OnClicked += Internally_OnClicked;
            UpdateLabels();

            // Edit name.
            Button editNameButton = FrontEndPanel.GetBaseButton(engineCore);
            editNameButton.DimensionInProcent = true;
            editNameButton.Width = 29;
            editNameButton.Height = 48;
            editNameButton.X = 70;
            editNameButton.Y = 50;
            editNameButton.Label.Text = "Name";
            editNameButton.OnClicked += EditNameButton_OnClicked;

            panel.DimensionInProcent = false;
            panel.Height = 80;
            panel.AddComponent(itemPanel);
            panel.AddComponent(mInternally);
            panel.AddComponent(label);
            panel.AddComponent(editNameButton);

            ItemPanel = panel;
        }

        private void EditNameButton_OnClicked(object sender, double x, double y, int id)
        {
            if (mParameter != null)
            {
                SharpDepend.Manager.DialogManager.Input("Rename parameter", "Rename the parameter '" + mParameter.Label + "'", mParameter.Label, RenameResult, mParameter);
            }
            else if (mIO != null)
            {
                SharpDepend.Manager.DialogManager.Input("Rename parameter", "Rename the parameter '" + mIO.ShortName + "'", mIO.ShortName, RenameResult, mIO);
            }
        }

        void RenameResult(bool success, string input, object parameter)
        {
            if (success)
            {
                // Rename parameter.
                if (parameter == mParameter)
                {
                    mParameter.Label = input;
                }
                else if (parameter == mIO)
                {
                    mIO.ShortName = input;
                }

                // Update module UI.
                mModulizerUI.UpdateModuleUIUp(mModule);
            }
        }

        private void Internally_OnClicked(object sender, double x, double y, int id)
        {
            Button button = sender as Button;

            if (mParameter != null)
            {
                mParameter.InternallyVisible = !mParameter.InternallyVisible;

                // Refresh visibility of parameters for the dsp.
                mModulizerUI.UpdateParameterStates();

                UpdateInternallyColor(button, mParameter.InternallyVisible);
            }
            else
            {
                // Check if changing internally is possible.
                bool notPossible = IsConnectedToGlobalModule(mIO);
                if (notPossible)
                {
                    SharpDepend.Manager.DialogManager.Show("Input or Output can not change to public when it is connected to an module outside this group.", "Connection visible problem", SharpDepend.DialogIcon.Error);
                    return;
                }
                mIO.InternallyVisible = !mIO.InternallyVisible;

                UpdateInternallyColor(button, mIO.InternallyVisible);
            }

            UpdateLabels();

            // Update module UI.
            mModulizerUI.UpdateModuleUIUp(mModule);
        }

        private bool IsConnectedToGlobalModule(ModuleIO mIO)
        {
            if (mIO.IsConnected)
            {
                if (mIO is ModuleInput)
                {
                    ModuleInput input = mIO as ModuleInput;
                    bool inputIsConnectedToAModuleInThisGroup = (input.Module.ParentModule as GroupModule).SubModules.Contains(input.FromOutput.Module);
                    return !inputIsConnectedToAModuleInThisGroup;
                }
                else if (mIO is ModuleOutput)
                {
                    ModuleOutput output = mIO as ModuleOutput;
                    foreach (var input in output.ConnectedInputs)
                    {
                        bool inputIsConnectedToAModuleInThisGroup = (output.Module.ParentModule as GroupModule).SubModules.Contains(input.Module);
                        if (!inputIsConnectedToAModuleInThisGroup)
                        {
                            return !inputIsConnectedToAModuleInThisGroup;
                        }
                    }
                }
            }

            return false;
        }

        private void UpdateInternallyColor(Button button, bool internallyVisible)
        {
            if (internallyVisible)
            {
                button.BackgroundColor = ZquenceStudio3Host.Resources.GlobalColors.RedMarkedButton;
            }
            else
            {
                button.BackgroundColor = ZquenceStudio3Host.Resources.GlobalColors.ButtonColor;
            }
        }

        private void UpdateLabels()
        {
            bool interanlly = mParameter != null ? mParameter.InternallyVisible : mIO.InternallyVisible;

            if (interanlly)
            {
                mInternally.Label.Text = "Private";
            }
            else
            {
                mInternally.Label.Text = "Public";
            }
        }
    }

    class TopModuleEditPanel : Panel
    {
        private ModulizerUIv2 mModulizerUI;
        private BaseModule mModule;
        private Button mInternally;

        public TopModuleEditPanel(BasicGuiManager engineCore, ModulizerUIv2 modulizerUI) : base(engineCore)
        {
            mModulizerUI = modulizerUI;

            Events.OnScaleChanged += Events_OnScaleChanged;

            // Internally button.
            mInternally = FrontEndPanel.GetBaseButton(engineCore);
            mInternally.DimensionInProcent = false;
            mInternally.Width = 45;
            mInternally.Height = 32;
            mInternally.OnClicked += MInternally_OnClicked;
            AddComponent(mInternally);
        }

        private void Events_OnScaleChanged()
        {
            mInternally.Width = this.RealWidth;
        }

        private void MInternally_OnClicked(object sender, double x, double y, int id)
        {
            bool visible = mModulizerUI.FrontEnd.GetVisibility(mModule);

            mModulizerUI.FrontEnd.SetVisibility(mModule, !visible);

            UpdateLabels();
        }

        private void UpdateLabels()
        {
            bool visible = mModulizerUI.FrontEnd.GetVisibility(mModule);

            if (!visible)
            {
                mInternally.Label.Text = "Private";
            }
            else
            {
                mInternally.Label.Text = "Public";
            }
        }

        internal void SetModule(BaseModule module)
        {
            mModule = module;

            UpdateLabels();
        }
    }

    class ToolboxPanel : Panel
    {
        private ModulizerUIv2 mModulizerUI;
        private bool mFirstEditTimePassed;

        // Toolbar at the left.

        // Label at the top.
        private Label mLabel;

        // Module properties edit.
        private Listbox moduleList;
        private BaseModule mModuleForEdit;

        // No module selected, available modules to add.
        private Listbox mSelectedModuleProperties;

        // Module properties for top module.
        private TopModuleEditPanel mTopModuleEditPanel;

        public ToolboxPanel(BasicGuiManager engineCore, ModulizerUIv2 modulizerUI)
            : base(engineCore)
        {
            mModulizerUI = modulizerUI;

            CreateItemsPanel();
        }

        /// <summary>
        /// Module for edit. Pass null to show modules for add instead.
        /// </summary>
        /// <param name="module"></param>
        public void SetModuleForEdit(BaseModule module)
        {
            if (this.mModuleForEdit != module || !mFirstEditTimePassed)
            {
                this.mFirstEditTimePassed = true;

                mTopModuleEditPanel.SetModule(null);
                mModuleForEdit = module;

                if (module == null)
                {
                    mLabel.Text = "Available modules:";
                    moduleList.Visible = true;
                    mSelectedModuleProperties.Visible = false;
                    mTopModuleEditPanel.Visible = false;
                }
                else
                {
                    if (module.IsTopModule())
                    {
                        mLabel.Text = "Selected top module:";
                        mTopModuleEditPanel.Visible = true;
                        mTopModuleEditPanel.SetModule(module);
                        mSelectedModuleProperties.Y = 15;
                    }
                    else
                    {
                        mLabel.Text = "Selected module:";
                        mTopModuleEditPanel.Visible = false;
                        mSelectedModuleProperties.Y = 5;
                    }

                    moduleList.Visible = false;
                    mSelectedModuleProperties.Visible = true;

                    // Update module info.
                    UpdateModuleInfo();
                }
            }
        }

        private void UpdateModuleInfo()
        {
            mSelectedModuleProperties.Clear();

            ParameterEditItem.ToggleNextColor = false;

            // Add inputs.
            foreach (var parameter in mModuleForEdit.Inputs)
            {
                ParameterEditItem item = new ParameterEditItem(EngineCore, mModulizerUI, parameter);
                mSelectedModuleProperties.AddItem(item);
            }
            // Add parameters.
            foreach (var parameter in mModuleForEdit.Parameters)
            {
                ParameterEditItem item = new ParameterEditItem(EngineCore, mModulizerUI, parameter as ModuleParameter);
                mSelectedModuleProperties.AddItem(item);
            }
            // Add outputs.
            foreach (var parameter in mModuleForEdit.Outputs)
            {
                ParameterEditItem item = new ParameterEditItem(EngineCore, mModulizerUI, parameter);
                mSelectedModuleProperties.AddItem(item);
            }
        }

        private void CreateItemsPanel()
        {
            // Add label.
            mLabel = new Label(EngineCore);
            mLabel.Y = 2;
            mLabel.Height = 2.5;
            mLabel.AutoSize = true;
            mLabel.DimensionInProcent = true;
            mLabel.Width = 100;
            AddComponent(mLabel);

            InitAvailableModules();

            InitSelectedModule();

            InitmTopModuleEditPanel();
        }

        private void InitAvailableModules()
        {
            // Add module list.
            moduleList = new Listbox(EngineCore);
            moduleList.SetBackgroundColor(new Color(80, 40, 40, 40));
            moduleList.DimensionInProcent = true;
            moduleList.Width = 100;
            moduleList.Y = 5;
            moduleList.Height = 95;

            moduleList.BackgroundColor = new Color(100, 30, 30, 30);
            List<ModulesRegistration.ModuleInfo> modules = new List<ModulesRegistration.ModuleInfo>(ModulesRegistration.Modules);
            modules.Sort();
            foreach (var moduleInfo in modules)
            {
                ListboxItem item = new ListboxItem(EngineCore, moduleInfo.Display, moduleInfo.ModuleType);
                item.ItemPanel.Height = 23;
                moduleList.AddItem(item);
            }
            AddComponent(moduleList);
        }

        private void InitSelectedModule()
        {
            // Add list.
            mSelectedModuleProperties = new Listbox(EngineCore);
            mSelectedModuleProperties.SetBackgroundColor(new Color(80, 40, 40, 40));
            mSelectedModuleProperties.DimensionInProcent = true;
            mSelectedModuleProperties.Width = 100;
            mSelectedModuleProperties.Y = 5;
            mSelectedModuleProperties.Height = 95;
            AddComponent(mSelectedModuleProperties);
        }

        private void InitmTopModuleEditPanel()
        {
            mTopModuleEditPanel = new TopModuleEditPanel(EngineCore, mModulizerUI);
            mTopModuleEditPanel.SetBackgroundColor(new Color(80, 40, 40, 40));
            mTopModuleEditPanel.DimensionInProcent = true;
            mTopModuleEditPanel.Width = 100;
            mTopModuleEditPanel.Y = 5;
            mTopModuleEditPanel.Height = 95;
            AddComponent(mTopModuleEditPanel);
        }

        public Type SelectedModule
        {
            get
            {
                ListboxItem item = moduleList.GetSelectedItem();

                if (item == null)
                {
                    return null;
                }

                return (Type)item.ItemObject;
            }
        }
    }
}
